const path = require('path');
const _ = require('lodash');
const fs = require('fs');
const { cp, mkdir } = require('shelljs');
const patchConstants = require('./patchConstants');
const {
  // getMatchRowNumber,
  insertInLineNo,
  getComponentsName,
  getComponentsImport,
  makeMockArray,
  makeMockObject,
  eslintFormat,
} = require('./utils');

const copySubComponents = (otherComponents) => {
  if (otherComponents && otherComponents.length) {
    const targetRootDir = path.join(
      global.cwdPath,
      global.config.componentsDir
    );
    const isDirExist = fs.existsSync(targetRootDir);
    if (isDirExist) {
      otherComponents.forEach((comp) => {
        // 先查 componentsDir 中是否有该组件
        const targetDir = path.join(targetRootDir, comp);
        console.log('targetDir', targetDir);
        const targetCompPath = path.join(targetDir, '/index.tsx');
        const isCompExsit = fs.existsSync(targetCompPath);
        console.log('isCompExsit', isCompExsit);

        if (!isCompExsit) {
          mkdir(targetDir);
          const source = path
            .join(global.appRoot, `hbs/__components/${comp}/`)
            .replace(/\\/g, '/');

          cp('-R', source, targetRootDir);
        }
      });
    } else {
      mkdir(targetRootDir);
      copySubComponents(otherComponents);
    }
  }
};

const handleEvent = (json, type) => {
  const { evts = [], hbs_path, target, componentName, template } = json;
  const hbs_target_path = path
    .join(target, `./${componentName}/index.tsx`)
    .replace(/\\/g, '/');

  if (type !== 'mobx' && type !== 'redux') return;

  const jsx = fs.readFileSync(hbs_path, { encoding: 'utf-8' });
  const { insertString, declarations, importStatement } = patchConstants(
    jsx.trim()
  );
  const dir = `${target}/${componentName}`;
  // 复制到target
  const isDirExsit = fs.existsSync(dir);
  if (!isDirExsit) {
    mkdir('-p', dir);
  }
  fs.writeFileSync(hbs_target_path, insertString, { encoding: 'utf-8' });
  // cp(hbs_path, hbs_target_path);

  /* ------useEffect初始化-------*/
  // const lineNo = getMatchRowNumber('FC<', hbs_path)[0]; //
  const initEvents = evts.filter((evt) => evt.init); // 先从json中拿到需要初始化调用的事件
  // 初始化的事件包裹到useEffect后插入到组件中
  let str = `const ${componentName}: FC<{}> = () => {\\n`;
  str += declarations;
  str +=
    type === 'redux'
      ? `const dispatch = useDispatch();const store = useSelector((state: any) => state.${componentName});`
      : '';

  str += 'useEffect(() => {\\n';
  initEvents.forEach((evt) => {
    const { name } = evt;
    str += type === 'redux' ? `dispatch(${name});` : `store.${name}();`;
  });
  str += '}, []);';
  // TODO: str+=declare
  str += 'return (\\n';

  if (type === 'mobx') {
    str += '<Observer>\\n{() => (';
  }

  const tempS = str.replace(/\s/g, ' ').replace(/;/g, ';\\n');
  // console.log('tempS', tempS);

  insertInLineNo(1, tempS, hbs_target_path);

  /* ------组件的注册事件---------*/

  /* -------头部插入-------*/
  str = '';
  // 固定引用的一些东西:
  str += `import { FC, useEffect } from 'react';${
    // redux/mobx库里的函数
    type === 'redux'
      ? "import { useSelector, useDispatch } from 'react-redux';import { configureStore } from '@reduxjs/toolkit';"
      : "import { Observer } from 'mobx-react-lite';"
  }`;

  // redux引入的action,reducer
  if (type === 'redux') {
    str += `import {${evts
      .map((e) => e.name)
      .join(',')}} from './store/actions';`;
  }

  str += importStatement;

  // 先找所有的组件名字
  // yh-design ，注意一些组件的子组件
  const componentsName = getComponentsName(jsx);
  const { ret, otherComponents, styledComponents } =
    getComponentsImport(componentsName);
  str += ret;
  copySubComponents(otherComponents);

  // 如果有样式，就复制样式文件
  if (styledComponents.length && template) {
    const p = path
      .join(global.appRoot, `hbs/${template}/style.hbs`)
      .replace(/\\/g, '/');
    const isExsit = fs.existsSync(p);
    if (isExsit) {
      cp(p, `${dir}/styled.ts`);
    } else {
      console.error(`no ${componentName}'s Style`);
    }
  }

  /* ---------组件外部定义的变量------------*/
  // 构造store
  str += '\\n';
  if (type === 'redux') {
    str += `import Reducer from './store/reducer';const store = configureStore({reducer: { ${componentName}: Reducer,},});`;
  } else {
    str += "import store from './store';";
  }

  insertInLineNo(1, str.replace(/;/g, ';\\n'), hbs_target_path);

  str = '';
  if (type === 'mobx') {
    str += `
)}
</Observer>`;
  }

  str += `)}
  
  export default ${componentName};
  `;
  fs.appendFileSync(hbs_target_path, str, { encoding: 'utf-8' });
};

const getEvtsProperty = (evts) => {
  // 抽取state [{state:'',name:'',type:array|object,init:bool}]
  const ret = [];
  evts.forEach((evt) => {
    const { name } = evt;
    const obj = { ...evt };
    if (name.includes('get')) {
      obj.state = _.lowerFirst(name.split('get')[1]);
      obj.cate = 'get';
    } else if (name.includes('submit')) {
      obj.state = _.lowerFirst(name.split('submit')[1]);
      obj.cate = 'submit';
    }
    ret.push(obj);
  });
  return ret;
};

const renderMobx = (json) => {
  const { componentName } = json;
  const targetPath = path
    .join(json.target, `./${componentName}/store.ts`)
    .replace(/\\/g, '/');
  // hbs
  let ret = `import { makeAutoObservable, runInAction } from 'mobx';
class ${componentName}Store {`;
  const evts = getEvtsProperty(json.evts || []);
  ret += `\n${evts
    .map((e) => `public ${e.state} : any = ${e.type === 'array' ? '[]' : '{}'}`)
    .join(';\n')}`;

  // 固定 constructor
  ret += `
constructor() {makeAutoObservable(this);}`;
  // evt循环渲染
  evts.forEach((e) => {
    ret += `
  public ${e.name}(${e.cate === 'submit' ? 'd:any' : ''}){
    runInAction(() => {
    this.${e.state} = ${
      e.cate === 'get'
        ? JSON.stringify(
            e.type === 'array'
              ? makeMockArray(e.dataKeys && e.dataKeys, e.len, e.dataKeys)
              : makeMockObject(e.len, e.dataKeys)
          )
        : 'd;'
    }
  });
  }\n`;
  });
  // 尾部 export default new ${componentName}Store();
  ret += `}
export default new ${componentName}Store();
`;
  fs.writeFileSync(targetPath, ret, { encoding: 'utf-8' });
};

const renderRedux = (json) => {
  const { target, componentName } = json;
  const dirPath = `${target}/${componentName}`;
  const evts = getEvtsProperty(json.evts || []);
  mkdir(path.join(dirPath, './store/'));
  let targetDir = path.join(dirPath, './store/actionType.ts');
  // getEvtsProperty
  let ret = '';
  /* ---------action-type-------*/

  // name大写循环渲染字符串
  const actionTypes = [];
  ret += evts
    .map((e) => {
      const actionType = _.toUpper(e.state);
      const actionTypeString = `${
        e.cate === 'get' ? 'GET' : 'SUBMIT'
      }_${actionType}`;
      actionTypes.push(actionTypeString);
      return `export const ${actionTypeString}='${actionTypeString}';\n`;
    })
    .join('');
  // 写入文件夹
  fs.writeFileSync(targetDir, ret, { encoding: 'utf-8' });

  /* ---------action-------*/
  ret = '';
  targetDir = path.join(dirPath, './store/actions.ts');
  // 引入action
  ret += `import {${actionTypes.join(',')}} from './actionType'\n`;

  ret += evts
    .map(
      (e, i) => `
export const ${e.name} = (${e.cate === 'submit' ? 'data:any' : ''}) => {
  return async (dispatch:Function)=>{
    ${
      e.cate === 'get'
        ? `const data = ${JSON.stringify(
            e.type === 'array'
              ? makeMockArray(
                  e.dataKeys && e.dataKeys.length,
                  e.len,
                  e.dataKeys
                )
              : makeMockObject(e.len, e.dataKeys)
          )};`
        : ''
    }
    dispatch({
      type:${actionTypes[i]},
      data
    })
  }
}

`
    )
    .join('');

  fs.writeFileSync(targetDir, ret, { encoding: 'utf-8' });

  /* ---------reducer.ts-------*/
  targetDir = path.join(dirPath, './store/reducer.ts');

  ret = '';
  ret += `import {${actionTypes.join(',')}} from './actionType'\n`;
  ret += `
type Action = {
  type: string;
  data?: any;
}`;
  ret += `\nconst defaultState = {
${evts
  .map((e) => `\t${e.state}: ${e.type === 'array' ? '[]' : '{}'}`)
  .join(',\n')}
};`;

  ret += `
const Reducer = (state = defaultState, action: Action) => {
  switch (action.type) {
${evts
  .map(
    (e, i) => `
    case ${actionTypes[i]}:
      return { ...state, ${e.state}: action.data};
`
  )
  .join('\n')}
    default:
      return state;
  }
}`;
  ret += '\nexport default Reducer;';
  fs.writeFileSync(targetDir, ret, { encoding: 'utf-8' });
};

const handleRedux = (json) => {
  handleEvent(json, 'redux');
  renderRedux(json);
  const p = path.join(json.target, json.componentName, '/').replace(/\\/g, '/');
  eslintFormat(p);
};

const handleMobx = (json) => {
  handleEvent(json, 'mobx');
  renderMobx(json);
  const p = path.join(json.target, json.componentName, '/').replace(/\\/g, '/');
  eslintFormat(p);
};

module.exports = {
  handleRedux,
  handleMobx,
};
